import matplotlib.pyplot as plt
import numpy as np
from tensorflow.python.framework import ops
from sklearn import svm
from sklearn.preprocessing import StandardScaler  # 特征缩放
import joblib as jlb  # 保存模型
# 设置字体为楷体
import matplotlib

import tensorflow.compat.v1 as tf
tf.disable_v2_behavior()

matplotlib.rcParams['font.sans-serif'] = ['KaiTi']
from data_format import x_train, y_train, x_cross, y_cross, x_test, y_test
from svm_prdiction_main import svm_prediction_method
import warnings

warnings.filterwarnings('ignore')  # "error", "ignore", "always", "default", "module" or "once"


def SVM_train(x_train, y_train, x_cross, y_cross, x_test, y_test):
    # 特征缩放
    sc = StandardScaler()
    x_train = sc.fit_transform(x_train)
    x_cross = sc.fit_transform(x_cross)
    x_test = sc.fit_transform(x_test)

    # 将训练好的模型保存
    random_state = np.random.RandomState(0)
    classfier = svm.SVC(kernel='linear', probability=True, random_state=random_state)
    classfier.fit(x_train, y_train)
    jlb.dump(classfier, './models/svm.pkl')


def show_change_SVM():
    # 创建图
    ops.reset_default_graph()

    sess = tf.Session()
    # Declare batch size
    batch_size = 164

    class1_x = [x[0] for i, x in enumerate(x_train) if y_train[i] == 1]
    class1_y = [x[2] for i, x in enumerate(x_train) if y_train[i] == 1]
    class2_x = [x[0] for i, x in enumerate(x_train) if y_train[i] == 0]
    class2_y = [x[2] for i, x in enumerate(x_train) if y_train[i] == 0]

    # Initialize placeholders
    x_data = tf.placeholder(shape=[None, 29], dtype=tf.float32)
    y_target = tf.placeholder(shape=[None, 1], dtype=tf.float32)
    prediction_grid = tf.placeholder(shape=[None, 29], dtype=tf.float32)
    grid = tf.placeholder(shape=[None, 2], dtype=tf.float32)
    # Create variables for svm
    b = tf.Variable(tf.random_normal(shape=[1, batch_size]))

    # Apply kernel
    # Linear Kernel
    # my_kernel = tf.matmul(x_data, tf.transpose(x_data))
    # Gaussian (RBF) kernel
    gamma = tf.constant(-50.0)
    dist = tf.reduce_sum(tf.square(x_data), 1)
    dist = tf.reshape(dist, [-1, 1])
    sq_dists = tf.add(tf.subtract(dist, tf.multiply(2., tf.matmul(x_data, tf.transpose(x_data)))), tf.transpose(dist))
    my_kernel = tf.exp(tf.multiply(gamma, tf.abs(sq_dists)))

    # Compute SVM Model
    first_term = tf.reduce_sum(b)
    b_vec_cross = tf.matmul(tf.transpose(b), b)
    y_target_cross = tf.matmul(y_target, tf.transpose(y_target))
    second_term = tf.reduce_sum(tf.multiply(my_kernel, tf.multiply(b_vec_cross, y_target_cross)))
    loss = tf.negative(tf.subtract(first_term, second_term))

    # Gaussian (RBF) prediction kernel
    rA = tf.reshape(tf.reduce_sum(tf.square(x_data), 1), [-1, 1])
    rB = tf.reshape(tf.reduce_sum(tf.square(prediction_grid), 1), [-1, 1])
    pred_sq_dist = tf.add(tf.subtract(rA, tf.multiply(2., tf.matmul(x_data, tf.transpose(prediction_grid)))),
                          tf.transpose(rB))
    pred_kernel = tf.exp(tf.multiply(gamma, tf.abs(pred_sq_dist)))
    prediction_output = tf.matmul(tf.multiply(tf.transpose(y_target), b), pred_kernel)
    prediction = tf.sign(prediction_output - tf.reduce_mean(prediction_output))
    accuracy = tf.reduce_mean(tf.cast(tf.equal(tf.squeeze(prediction), tf.squeeze(y_target)), tf.float32))

    # Declare optimizer
    my_opt = tf.train.GradientDescentOptimizer(0.002)
    train_step = my_opt.minimize(loss)

    # Initialize variables
    init = tf.global_variables_initializer()
    sess.run(init)

    # Training loop
    loss_vec = []
    batch_accuracy = []

    for i in range(1000):
        rand_index = np.random.choice(len(x_train), size=batch_size)
        rand_x = x_train[rand_index]
        rand_y = np.transpose([y_train[rand_index]])
        sess.run(train_step, feed_dict={x_data: rand_x, y_target: rand_y})
        temp_loss = sess.run(loss, feed_dict={x_data: rand_x, y_target: rand_y})
        loss_vec.append(temp_loss)
        acc_temp = sess.run(accuracy, feed_dict={x_data: rand_x,
                                                 y_target: rand_y,
                                                 prediction_grid: rand_x})
        batch_accuracy.append(acc_temp)
        if (i + 1) % 250 == 0:
            print('Step #' + str(i + 1))
            print('Loss = ' + str(temp_loss))

    # Plot batch accuracy
    plt.plot(batch_accuracy, 'k-', label='Accuracy')
    plt.title('Batch Accuracy')
    plt.xlabel('Generation')
    plt.ylabel('Accuracy')
    plt.legend(loc='lower right')
    plt.show()

    # Plot loss over time
    plt.plot(loss_vec, 'k-')
    plt.title('Loss per Generation')
    plt.xlabel('Generation')
    plt.ylabel('Loss')
    plt.show()


def SVM_result():
    svm_prediction_method()


if __name__ == '__main__':
    SVM_result()
